package com.jfirer.jnet.extend.http.coder;

import com.jfirer.baseutil.IoUtil;
import com.jfirer.baseutil.RuntimeJVM;
import com.jfirer.baseutil.STR;
import com.jfirer.baseutil.StringUtil;
import com.jfirer.jnet.common.api.ReadProcessor;
import com.jfirer.jnet.common.api.ReadProcessorNode;
import com.jfirer.jnet.common.util.HttpDecodeUtil;
import com.jfirer.jnet.extend.http.dto.FullHttpResp;
import com.jfirer.jnet.extend.http.dto.HttpRequest;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Slf4j
public abstract class AbstractResourceEncoder implements ReadProcessor<HttpRequest>
{
    //资源地址的文件夹路径
    protected final String resourcePathPrefix;
    /**
     * 请求的url的前缀
     * 比如请求url的前缀是/dist，资源地址的文件夹路径是a，那么一个请求路径为/dist/index.html的文件应该在实际的a/index.html来寻找。
     */
    protected final String urlPrefix;
    protected final int    urlPrefixLength;

    public AbstractResourceEncoder(String resourcePathPrefix, String urlPrefix)
    {
        this.resourcePathPrefix = resourcePathPrefix;
        this.urlPrefix          = urlPrefix;
        urlPrefixLength         = urlPrefix.length();
    }

    public static AbstractResourceEncoder from(String resource, String urlPrefix)
    {
        if (resource.startsWith("classpath:"))
        {
            return new ClassResourceEncoder(resource.substring(10), urlPrefix);
        }
        else if (resource.startsWith("file:"))
        {
            return new FileResourceEncoder(resource.substring(5), urlPrefix);
        }
        else
        {
            throw new IllegalArgumentException();
        }
    }

    @Override
    public void read(HttpRequest request, ReadProcessorNode next)
    {
        String url = request.getUrl();
        if (!request.getMethod().equalsIgnoreCase("get"))
        {
            next.fireRead(request);
        }
        else
        {
            if (urlPrefixLength != 0)
            {
                if (!url.startsWith(urlPrefix))
                {
                    next.fireRead(request);
                    return;
                }
                else
                {
                    url = url.substring(urlPrefixLength);
                }
            }
            url = HttpDecodeUtil.pureUrl(url);
            if (StringUtil.isBlank(url))
            {
                url = "index.html";
            }
            else if (url.equals("/"))
            {
                url = "/index.html";
            }
            process(request, url, next);
        }
    }

    protected abstract void process(HttpRequest request, String url, ReadProcessorNode next);

    public static class ClassResourceEncoder extends AbstractResourceEncoder
    {
        record StaticResource(byte[] content, String contentType)
        {
        }

        private ConcurrentMap<String, StaticResource> map = new ConcurrentHashMap<>();

        public ClassResourceEncoder(String resourcePathPrefix, String urlPrefix)
        {
            super(resourcePathPrefix, urlPrefix);
        }

        @Override
        protected void process(HttpRequest request, String url, ReadProcessorNode next)
        {
            StaticResource staticResource = map.computeIfAbsent(url, str -> {
                String contentType           = HttpDecodeUtil.findContentType(str);
                String realClassResourcePath = resourcePathPrefix + str;
                log.trace("当前请求路径为:{}", realClassResourcePath);
                try (InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(realClassResourcePath))
                {
                    if (resourceAsStream != null)
                    {
                        return new StaticResource(IoUtil.readAllBytes(resourceAsStream), contentType);
                    }
                    else
                    {
                        return null;
                    }
                }
                catch (IOException e)
                {
                    throw new RuntimeException(e);
                }
            });
            if (staticResource == null)
            {
                next.fireRead(request);
            }
            else
            {
                request.close();
                FullHttpResp response = new FullHttpResp();
                response.getHead().addHeader("Content-Type", staticResource.contentType());
                response.getBody().setBodyBytes(staticResource.content());
                response.getHead().addHeader("Cache-Control", "max-age=3600");
                next.pipeline().fireWrite(response);
            }
        }
    }

    public static class FileResourceEncoder extends AbstractResourceEncoder
    {
        protected final File dir;

        public FileResourceEncoder(String resourcePathPrefix, String urlPrefix)
        {
            super(resourcePathPrefix, urlPrefix);
            File dirOfMainClass = RuntimeJVM.getDirOfMainClass();
            dir = new File(dirOfMainClass, resourcePathPrefix);
            if (dir.exists() == false || dir.isDirectory() == false)
            {
                throw new IllegalArgumentException(STR.format("配置的路径:{}不存在或不是文件夹", dir.getAbsolutePath()));
            }
        }

        @Override
        protected void process(HttpRequest request, String url, ReadProcessorNode next)
        {
            File target = new File(dir, url);
            if (!target.exists())
            {
                next.fireRead(request);
            }
            else
            {
                request.close();
                String contentType = HttpDecodeUtil.findContentType(url);
                try (FileInputStream inputStream = new FileInputStream(target))
                {
                    FullHttpResp response = new FullHttpResp();
                    response.getHead().addHeader("Content-Type", contentType);
                    response.getBody().setBodyBytes(IoUtil.readAllBytes(inputStream));
                    next.pipeline().fireWrite(response);
                }
                catch (IOException e)
                {
                    FullHttpResp response = new FullHttpResp();
                    response.getHead().addHeader("Content-Type", "text");
                    response.getBody().setBodyText("error:" + e.getMessage());
                    next.pipeline().fireWrite(response);
                }
            }
        }
    }
}
